from django.db import models
import random

# Create your models here.

class Skill(models.Model):
    ATTR_CHOICES = (
        ('str', 'Strength'),
        ('dex', 'Dextery'),
        ('con', 'Constitution'),
        ('int', 'Intelligence'),
        ('wis', 'Wisdom'),
        ('cha', 'Charisma'),
    )

    name = models.CharField(max_length=30)
    attr = models.CharField(max_length=3, choices=ATTR_CHOICES)
    
    def __unicode__(self):
        return self.name

class HardcodeSkills:
    arcana_ins = None
    def __arcana__(self):
        if not self.arcana_ins:
            self.arcana_ins = Skill.objects.create(name='Arcana', attr='int')
        return self.arcana_ins
    
    arcana = property(__arcana__)
    
    diplomacy_ins = None
    def __diplomacy__(self):
        if not self.diplomacy_ins:
            self.diplomacy_ins = Skill.objects.create(name = 'Diplomacy', attr = 'cha')
        return self.diplomacy_ins
    
    diplomacy = property(__diplomacy__)
    
    stealth_ins = None
    def __stealth__(self):
        if not self.stealth_ins:
            self.stealth_ins = Skill.objects.create(name = 'Stealth', attr = 'dex')
        return self.stealth_ins
    
    stealth = property(__stealth__)

class SkillTest(models.Model):
    skill = models.ForeignKey(Skill)
    dc = models.IntegerField(default=8)
    complete = models.BooleanField(default=False)
    completer = models.ForeignKey('Character', null=True)
    challenge = models.ForeignKey('SkillChallenge', null=True)

    def __unicode__(self):
        status = 'incomplete'
        if (self.complete):
            status = 'complete'
        return self.skill + ' : ' + str(self.dc) + ' : ' + status

    def test(self, character):
        """
        make test to decide whether the character succeeded
        
        # create a character, skill and test
        >>> hs = HardcodeSkills()
        >>> bill = Character.objects.create(name='bill', int=14)
        >>> bill.train(hs.arcana)
        >>> test = SkillTest.objects.create(skill=hs.arcana, dc=27)
        
        # create a random stub
        >>> import random
        
        # this will make random.randint(1,20) return always 3 in the first run
        >>> random.seed(1)
        >>> test.test(bill)
        False
        >>> test.complete
        False
        >>> test.completer
        
        # then make the test pass, seed = 2 will make random.randit return 20 in the first run
        >>> random.seed(2)
        >>> test.test(bill)
        True
        >>> test.complete
        True
        >>> test.completer
        <Character: bill>
        """
        bonus = character.skill_bonus(self.skill)
        check = random.randint(1,20) + bonus
        if check >= self.dc:
            self.complete = True
            self.completer = character            
        return self.complete 

class SkillChallenge(models.Model):
    name = models.CharField(max_length=30, null=True)
    keywords = models.TextField(null=True)
    success_need = models.IntegerField(default=4)
    failures_limit = models.IntegerField(default=2)
    success_scored = models.IntegerField(default=0)
    failures_scored = models.IntegerField(default=0)
    
    def __unicode__(self):
        return name + ' : ' + str(self.complexity)
    
    def __complexity__(self):
        """
        >>> s = SkillChallenge()
        >>> s.complexity
        1
        >>> s.success_need = 6
        >>> s.complexity
        2
        >>> s.success_need = 8
        >>> s.complexity
        3
        >>> s.success_need = 10
        >>> s.complexity
        4
        >>> s.success_need = 12
        >>> s.complexity
        5
        """
        return self.success_need/2 - 1
        
    complexity = property(__complexity__)
    
    def __complete__(self):
        """
        >>> s = SkillChallenge()
        >>> s.complete
        False
        >>> s.success_scored = s.success_need
        >>> s.complete
        True
        """
        return self.success_scored == self.success_need
        
    complete = property(__complete__)

    def __failed__(self):
        """
        >>> s = SkillChallenge()
        >>> s.failed
        False
        >>> s.failures_scored = s.failures_limit
        >>> s.failed
        True
        """
        return self.failures_scored == self.failures_limit
        
    failed = property(__failed__)

    def test(self, skilltest, character):
        """
        >>> challenge = SkillChallenge.objects.create()
        >>> challenge.success_scored
        0
        >>> challenge.failures_scored
        0
        
        # create some character, skill and test
        >>> hs = HardcodeSkills()
        >>> bill = Character.objects.create(name='bill', int=14)
        >>> bill.train(hs.arcana)
        >>> test = SkillTest.objects.create(skill=hs.arcana, dc=27, challenge=challenge)
        
        # let's test the skill test!
        >>> import random
        >>> random.seed(1)
        >>> challenge.test(test, bill)
        False
        >>> challenge.success_scored
        0
        >>> challenge.failures_scored
        1
        >>> random.seed(2)
        >>> challenge.test(test, bill)
        True
        >>> challenge.success_scored
        1
        >>> challenge.failures_scored
        1
        """
        result = False
        if skilltest.test(character):
            self.success_scored += 1
            result = True
        else:
            self.failures_scored += 1
        return result

class Character(models.Model):
    name = models.CharField(max_length=255, blank=True, default='')
    str = models.IntegerField(default=8)
    dex = models.IntegerField(default=8)
    con = models.IntegerField(default=8)
    int = models.IntegerField(default=8)
    wis = models.IntegerField(default=8)
    cha = models.IntegerField(default=8)
    skill_features = models.ManyToManyField(Skill, through='SkillFeature')
    
    def __unicode__(self):
        return self.name
    
    def __attr_bonus__(self, attr_value):
        """
        returns the bonus for an attribute value as in:
        
        >>> Character().__attr_bonus__(6)
        -2
        >>> Character().__attr_bonus__(7)
        -2
        >>> Character().__attr_bonus__(8)
        -1
        >>> Character().__attr_bonus__(9)
        -1
        >>> Character().__attr_bonus__(10)
        0
        >>> Character().__attr_bonus__(11)
        0
        >>> Character().__attr_bonus__(12)
        1
        >>> Character().__attr_bonus__(13)
        1
        >>> Character().__attr_bonus__(14)
        2
        >>> Character().__attr_bonus__(15)
        2
        >>> Character().__attr_bonus__(16)
        3
        >>> Character().__attr_bonus__(17)
        3
        >>> Character().__attr_bonus__(18)
        4
        >>> Character().__attr_bonus__(19)
        4
        >>> Character().__attr_bonus__(20)
        5
        """
        return attr_value/2 - 5
    
    def skill_bonus(self, skill):
        """
        # let's say bill know how to hide
        >>> hs = HardcodeSkills()
        >>> bill = Character.objects.create(name = 'bill', dex = 14, cha = 9)
        >>> bill.train(hs.stealth)
        
        # then bill's stealth score should be +5 + 2(dex) = +7
        >>> bill.skill_bonus(hs.stealth)
        7
        
        # and btw, diplomacy should be +0 (untrained) -1 (cha) = -1
        >>> bill.skill_bonus(hs.diplomacy)
        -1
        
        >>> halfling = SkillFeature.objects.create(character=bill, skill=hs.diplomacy, bonus=2, feature='halfling racial')
        >>> bill.skill_bonus(hs.diplomacy)
        1
        """
        result = 0
        for feature in self.skillfeature_set.filter(skill = skill):
            result += feature.bonus
        result += self.__attr_bonus__(eval('self.'+skill.attr))
        return result
        
    def train(self, skill):
        """
        >>> hs = HardcodeSkills()
        >>> bill = Character.objects.create(name='bill')
        >>> bill.train(hs.arcana)
        >>> hs.arcana in bill.skill_features.all()
        True
        """
        SkillFeature.objects.create(character=self, skill=skill, bonus=5, feature='trainning')

class SkillFeature(models.Model):
    character = models.ForeignKey(Character)
    skill = models.ForeignKey(Skill)
    bonus = models.IntegerField(default=0)
    feature = models.CharField(max_length=30)
    

